#ifndef PW_PBL_META_AUTO_HPP
#define PW_PBL_META_AUTO_HPP

#include "String.hpp"

/// 元编程相关定义
/// 元编程类型主要有两大类：
///  - 字面量类，类型内部包含数据，可根据输入数据自动推导转换，可作为模板的参数
///  - 值产生类，类型为空类型，值产生于模板参数中
/// 在literals名字空间中包含用于便捷生成值产生类型的常量值，且包含在pw_pbl名字空间中，即pw_pbl::Meta::Name类型通常有对应的pw_pbl::MetaName常量
namespace pw_pbl::Meta
{
	/// 自动字面量，主要用于增强template<auto...>可接受值的范围，template<Auto...>理论上可接受任意字面量值
	template <typename... T>
	struct Auto;

	template <typename T>
	struct Auto<T>
	{
		using type = T;

		T value;
		consteval Auto(T v) noexcept : value{v} {}

		constexpr bool operator==(const Auto &) const = default;
		constexpr bool operator==(auto) const { return false; }

		constexpr operator decltype(auto)() const noexcept { return value; }
		constexpr operator decltype(auto)() noexcept { return value; }
		constexpr auto &operator()() const noexcept { return value; }
		constexpr auto &operator()() noexcept { return value; }
	};

	template <typename T> requires std::is_class_v<T>
	struct Auto<T> : public T
	{
		using type = T;

		using T::T;
		constexpr Auto(const Auto &) = default;
		constexpr Auto(Auto &&) = default;
		template <typename... NT>
		constexpr Auto(NT &&...v) requires requires { T{std::forward<NT>(v)...}; } : T{std::forward<NT>(v)...} {}

		constexpr bool operator==(const Auto &) const = default;
		constexpr bool operator==(auto) const { return false; }

		constexpr operator decltype(auto)() const noexcept { return *static_cast<const T *>(this); }
		constexpr operator decltype(auto)() noexcept { return *static_cast<T *>(this); }
		constexpr auto &operator()() const noexcept { return *static_cast<const T *>(this); }
		constexpr auto &operator()() noexcept { return *static_cast<T *>(this); }
	};

	template <typename T>
	Auto(T &&) -> Auto<T>;

	template <size_t N>
	Auto(const char (&)[N]) -> Auto<Str<char, N>>;

	template <size_t N>
	Auto(const Str<char, N> &) -> Auto<Str<char, N>>;

	/// 名字、值对字面量类，用在template<NVPair>时可接受struct<{"name", value}>形式的输入
	template <typename CHAR, size_t N, typename T>
	struct NVPair
	{
		Str<CHAR, N> name;
		Auto<T> value;
	};
	template <typename CHAR, size_t N, typename T>
	NVPair(const CHAR (&)[N], T &&t) -> NVPair<CHAR, N, typename decltype(Auto{static_cast<T &&>(t)})::type>;

	/// 值产生类，可选附带一个名字
	template <Auto V, Str... Name> requires(sizeof...(Name) <= 1)
	struct Value;

	template <Auto V>
	struct Value<V>
	{
		static constexpr auto &ref = V();
	};

	template <Auto V, Str Name>
	struct Value<V, Name>
	{
		static constexpr auto &ref = V();
		static constexpr auto &name = Name;
	};

	/// 类型产生类，可选附带一个名字
	template <typename T, Str... Name> requires(sizeof...(Name) <= 1)
	struct Type;

	template <typename T>
	struct Type<T>
	{
		using type = T;

		template <typename U>
		static constexpr Type<U> apply() noexcept { return {}; }
	};

	template <typename T, Str Name>
	struct Type<T, Name>
	{
		using type = T;
		static constexpr auto &name = Name;

		template <typename U>
		static constexpr Type<U, Name> apply() noexcept { return {}; }
	};

	inline namespace literals
	{
		template <Auto V>
		constexpr auto &MetaAuto = V();

		template <Auto V, Str... Name> requires(sizeof...(Name) <= 1)
		constexpr Meta::Value<V, Name...> MetaValue = {};

		template <typename T = Auto<>, Auto... V>
		extern constexpr auto MetaArray = std::array{V()...};

		template <typename CHAR, size_t N, Auto<Str<CHAR, N>>... V>
		extern constexpr auto MetaArray<Auto<>, V...> = std::array{V()...};

		template <typename CHAR, size_t N1, size_t... Nn, Auto<Str<CHAR, N1>> V1, Auto<Str<CHAR, Nn>>... Vn> requires((N1 != Nn) || ...)
		extern constexpr auto MetaArray<Auto<>, V1, Vn...> = std::array<StrRef<const CHAR>, sizeof...(Vn) + 1>{V1(), Vn()...};

		template <typename T, Auto... V> requires(!std::same_as<T, Auto<>>)
		extern constexpr auto MetaArray<T, V...> = std::array<T, sizeof...(V)>{V()...};

		template <typename T, Str... Name>
		extern constexpr Meta::Type<T, Name...> MetaType = {};
	} // namespace literals

} // namespace pw_pbl::Meta

namespace pw_pbl
{
	using namespace Meta::literals;
} // namespace pw_pbl
#endif // PW_PBL_META_AUTO_HPP
